package com.olive.interceptor;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import com.alibaba.fastjson.JSON;
import com.olive.entity.OpenApiDetailDO;
import com.olive.mapper.OpenApiDetailMapper;
import com.olive.request.RequestWrapper;
import com.olive.utils.IpUtils;
import com.olive.utils.LogUtil;
import com.olive.utils.ResultUtil;
import com.olive.utils.SignUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SignInterceptor implements HandlerInterceptor {

    private static final String ACCESSKEY = "access-key";//调用者身份唯一标识
    
    private static final String TIMESTAMP = "time-stamp";//时间戳
    
    private static final String SIGN = "sign";//签名
    
    private static final String NONCE = "nonce";//随机值

    @Resource
    private OpenApiDetailMapper openApiDetailMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(checkSign(request, response)){//签名认证
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
        return false;
    }

    /**
     * 验证签名
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    private boolean checkSign(HttpServletRequest request,HttpServletResponse response)throws Exception {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf8");
        String ip = IpUtils.getIpAddr(request);
        LogUtil.infoSafe("开放接口", "访问时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL());
        String accessKey = request.getHeader(ACCESSKEY);
        String timestamp = request.getHeader(TIMESTAMP);
        String nonce = request.getHeader(NONCE);
        String sign = request.getHeader(SIGN);
        if (!StringUtils.isNotBlank(accessKey)) {
            response.getWriter().write(JSON.toJSONString(ResultUtil.fail("accessKey无效")));
            LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:accessKey无效");
            return false;
        }
        if (StringUtils.isBlank(sign)) {
            response.getWriter().write(JSON.toJSONString(ResultUtil.fail("签名无效")));
            LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:签名无效");
            return false;
        }
        OpenApiDetailDO openApiDetailDO = openApiDetailMapper.getOneByAccessKey(accessKey);
        if (openApiDetailDO == null) {
            response.getWriter().write(JSON.toJSONString(ResultUtil.fail("accessKey不存在")));
            LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:accessKey不存在");
            return false;
        }
        if (StringUtils.isNotBlank(openApiDetailDO.getBlackList())) {
            for (String bIp : openApiDetailDO.getBlackList().split(",")) {
                if (bIp.equals(ip)) {//黑名单
                    response.getWriter().write(JSON.toJSONString(ResultUtil.fail("拒绝请求")));
                    LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:黑名单拒绝请求");
                    return false;
                }
            }
        }
        if (StringUtils.isNotBlank(openApiDetailDO.getWhiteList())) {
            boolean flag = false;
            for (String bIp : openApiDetailDO.getWhiteList().split(",")) {
                if (bIp.equals(ip)) {//白名单
                    flag = true;
                    break;
                }
            }
            if(!flag){
                response.getWriter().write(JSON.toJSONString(ResultUtil.fail("拒绝请求")));
                LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:白名单未符合拒绝请求");
                return false;
            }
        }

        if ("0".equals(openApiDetailDO.getInvokeStatus() + "")) {
            response.getWriter().write(JSON.toJSONString(ResultUtil.fail("访问权限已被冻结")));
            LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:访问权限已被冻结");
            return false;
        }
        if (!"1".equals(openApiDetailDO.getApiStatus() + "")) {
            response.getWriter().write(JSON.toJSONString(ResultUtil.fail("接口异常,暂停访问")));
            LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:接口异常,暂停访问");
            return false;
        }

        if (!StringUtils.isNotBlank(timestamp)) {
            response.getWriter().write(JSON.toJSONString(ResultUtil.fail("时间戳无效")));
            LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:时间戳无效");
            return false;
        } else if (openApiDetailDO.getTimeOut() != null) {
            if (System.currentTimeMillis() - Long.valueOf(timestamp) > openApiDetailDO.getTimeOut() * 1000) {
                response.getWriter().write(JSON.toJSONString(ResultUtil.fail("请求已过期")));
                LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:请求已过期");
                return false;
            }
        }
        Map<String, Object> hashMap = new HashMap<>();
        String queryStrings = request.getQueryString();//获取url后边拼接的参数
        if (queryStrings != null) {
            for (String queryString : queryStrings.split("&")) {
                String[] param = queryString.split("=");
                if (param.length == 2) {
                    hashMap.put(param[0], param[1]);
                }
            }
        }
        hashMap.put(ACCESSKEY, accessKey);
        hashMap.put(TIMESTAMP, timestamp);
        if (StringUtils.isNotBlank(nonce)) {
            hashMap.put(NONCE, nonce);
        }
        String secretKey = openApiDetailDO.getAppSecret();
        String body = new String(new RequestWrapper(request).getRequestBody());
        if (StringUtils.isNotBlank(body)) {
            Map<String, Object> map = JSON.parseObject(body);
            if (map != null) {
                hashMap.putAll(map);
            }
        }
        if (!SignUtil.signValidate(hashMap, secretKey, sign)) {//认证失败
            response.getWriter().write(JSON.toJSONString(ResultUtil.fail("认证失败")));
            LogUtil.errorSafe("开放接口请求失败", "时间:" + LocalDateTime.now() + ",IP:" + ip + ",访问接口:" + request.getRequestURL() + "错误信息:认证失败");
            return false;
        }
        return true;
    }
}
